package com.payment.utils;

import com.alibaba.fastjson.JSON;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.log4j.Logger;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * MD5加密算法
 *
 * @author lm
 * @date 2020/7/31 11:15
 */
public class Md5Encrypt {

    private static Logger logger = Logger.getLogger(Md5Encrypt.class);
    /**
     * Used building output as Hex
     */
    private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6',
            '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    /**
     * 对字符串进行MD5加密
     *
     * @param text 明文
     * @return 密文
     */
    public static String md5(String text) {
        MessageDigest msgDigest = null;

        try {
            msgDigest = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException(
                    "System doesn't support MD5 algorithm.");
        }

        try {
            msgDigest.update(text.getBytes("utf-8"));

        } catch (UnsupportedEncodingException e) {

            throw new IllegalStateException(
                    "System doesn't support your  EncodingException.");

        }

        byte[] bytes = msgDigest.digest();

        String md5Str = new String(encodeHex(bytes));

        return md5Str;
    }

    public static char[] encodeHex(byte[] data) {

        int l = data.length;

        char[] out = new char[l << 1];

        // two characters form the hex value.
        for (int i = 0, j = 0; i < l; i++) {
            out[j++] = DIGITS[(0xF0 & data[i]) >>> 4];
            out[j++] = DIGITS[0x0F & data[i]];
        }

        return out;
    }

    /**
     * 计算返回报文签名 for json
     */
    public static String jsonSign(Object obj) {
        Map<String, Object> map = dealMap(obj);
        //logger.info("排序前的map:" + map);
        //去掉添加的签名字段
        map.remove("sign");
        map = sortMapByKey(map);
        //logger.info("排序后的map:" + map);
        String headJson = JSON.toJSONString(map);
        String sign = md5(headJson);
        //logger.info("签名体：" + headJson + ";计算的签名：" + sign);
        return sign;
    }

    /**
     * 使用 Map按key进行排序
     *
     * @param map
     * @return
     */
    public static Map<String, Object> sortMapByKey(Map<String, Object> map) {
        if (map == null || map.isEmpty()) {
            return null;
        }
        Map<String, Object> sortMap = new TreeMap<String, Object>(new MapKeyComparator());
        sortMap.putAll(map);
        return sortMap;
    }

    /**
     * 将bean转换为map、并处理掉map中的null值，""值
     */
    private static Map<String, Object> dealMap(Object o) {
        Map<String, String> map = new HashMap<String, String>(16);
        try {
            map = BeanUtils.describe(o);
            map.put("class", null);
            //避免bean转map后多处class可以的签名，导致请求方的签名与这里签名的不一致
        } catch (Exception e) {
            logger.info("根据传入的实体类生成签名sign发送异常", e);
        }
        Set set = map.keySet();
        for (Iterator iterator = set.iterator(); iterator.hasNext(); ) {

            Object obj = iterator.next();
            Object value = map.get(obj);

            //if(!"".equals(value)){}：这句话的意思是空字符允许签名，不排除在外，仅仅排除null值。
            if (!"".equals(value)) {
                remove(value, iterator);
            }
        }
        Map tm = map;
        return new HashMap<String, Object>(tm);
    }

    /**
     * 移除map中的空值
     * Iterator 是工作在一个独立的线程中，并且拥有一个 mutex 锁。
     * Iterator 被创建之后会建立一个指向原来对象的单链索引表，当原来的对象数量发生变化时，这个索引表的内容不会同步改变，
     * 所以当索引指针往后移动的时候就找不到要迭代的对象，所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。
     * 所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。
     * 但你可以使用 Iterator 本身的方法 remove() 来删除对象， Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。
     *
     * @param obj
     * @param iterator
     */
    private static void remove(Object obj, Iterator iterator) {

        if (obj instanceof String) {
            if (obj == null || obj.toString().length() == 0) {
                iterator.remove();
            }
        } else if (obj instanceof Collection) {
            Collection col = (Collection) obj;
            if (col == null || col.isEmpty()) {
                iterator.remove();
            }
        } else if (obj instanceof Map) {
            Map temp = (Map) obj;
            if (temp == null || temp.isEmpty()) {
                iterator.remove();
            }
        } else if (obj instanceof Object[]) {
            Object[] array = (Object[]) obj;
            if (array.length <= 0 || array == null) {
                iterator.remove();
            }
        } else {
            if (obj == null) {
                iterator.remove();
            }
        }
    }

}
